/**
  ******************************************************************************
  * @file    usbd_cdc_core.c
  * @author  MCD Application Team
  * @version V1.2.0
  * @date    09-November-2015
  * @brief   This file provides the high layer firmware functions to manage the 
  *          following functionalities of the USB CDC Class:
  *           - Initialization and Configuration of high and low layer
  *           - Enumeration as CDC Device (and enumeration for each implemented memory interface)
  *           - OUT/IN data transfer
  *           - Command IN transfer (class requests management)
  *           - Error management
  *           
  *  @verbatim
  *      
  *          ===================================================================      
  *                                CDC Class Driver Description
  *          =================================================================== 
  *           This driver manages the "Universal Serial Bus Class Definitions for Communications Devices
  *           Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus 
  *           Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007"
  *           This driver implements the following aspects of the specification:
  *             - Device descriptor management
  *             - Configuration descriptor management
  *             - Enumeration as CDC device with 2 data endpoints (IN and OUT) and 1 command endpoint (IN)
  *             - Requests management (as described in section 6.2 in specification)
  *             - Abstract Control Model compliant
  *             - Union Functional collection (using 1 IN endpoint for control)
  *             - Data interface class

  *           @note
  *             For the Abstract Control Model, this core allows only transmitting the requests to
  *             lower layer dispatcher (ie. usbd_cdc_vcp.c/.h) which should manage each request and
  *             perform relative actions.
  * 
  *           These aspects may be enriched or modified for a specific user application.
  *          
  *            This driver doesn't implement the following aspects of the specification 
  *            (but it is possible to manage these features with some modifications on this driver):
  *             - Any class-specific aspect relative to communication classes should be managed by user application.
  *             - All communication classes other than PSTN are not managed
  *      
  *  @endverbatim
  *                                  
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc_core.h"
#include "usbd_desc.h"
#include "usbd_req.h"


/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @{
  */


/** @defgroup usbd_cdc 
  * @brief usbd core module
  * @{
  */

/** @defgroup usbd_cdc_Private_TypesDefinitions
  * @{
  */
/**
  * @}
  */


/** @defgroup usbd_cdc_Private_Defines
  * @{
  */

#define USB_CDC_IDLE 0
#define USB_CDC_BUSY 1
#define USB_CDC_ZLP  2

/**
  * @}
  */


/** @defgroup usbd_cdc_Private_Macros
  * @{
  */
/**
  * @}
  */


/** @defgroup usbd_cdc_Private_FunctionPrototypes
  * @{
  */

/*********************************************
   CDC Device library callbacks
 *********************************************/
uint8_t usbd_cdc_Init(void *pdev, uint8_t cfgidx);
uint8_t usbd_cdc_DeInit(void *pdev, uint8_t cfgidx);
uint8_t usbd_cdc_Setup(void *pdev, USB_SETUP_REQ *req);
uint8_t usbd_cdc_EP0_RxReady(void *pdev);
uint8_t usbd_cdc_DataIn(void *pdev, uint8_t epnum);
uint8_t usbd_cdc_DataOut(void *pdev, uint8_t epnum);
uint8_t usbd_cdc_SOF(void *pdev);

/*********************************************
   CDC specific management functions
 *********************************************/
static void     Handle_USBAsynchXfer(void *pdev);
static uint8_t *USBD_cdc_GetCfgDesc(uint8_t speed, uint16_t *length);
#ifdef USE_USB_OTG_HS
static uint8_t *USBD_cdc_GetOtherCfgDesc(uint8_t speed, uint16_t *length);
#endif
/**
  * @}
  */

/** @defgroup usbd_cdc_Private_Variables
  * @{
  */
extern CDC_IF_Prop_TypeDef APP_FOPS;
extern uint8_t             USBD_DeviceDesc[USB_SIZ_DEVICE_DESC];

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_OtherCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN static __IO uint32_t usbd_cdc_AltSet __ALIGN_END = 0;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t USB_Rx_Buffer[CDC_DATA_MAX_PACKET_SIZE] __ALIGN_END;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t APP_Rx_Buffer[APP_RX_DATA_SIZE] __ALIGN_END;


#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END;

uint32_t APP_Rx_ptr_in  = 0;
uint32_t APP_Rx_ptr_out = 0;
uint32_t APP_Rx_length  = 0;

uint8_t USB_Tx_State = USB_CDC_IDLE;

static uint32_t cdcCmd = 0xFF;
static uint32_t cdcLen = 0;

/* CDC interface class callbacks structure */
USBD_Class_cb_TypeDef USBD_CDC_cb =
    {
        usbd_cdc_Init,
        usbd_cdc_DeInit,
        usbd_cdc_Setup,
        NULL, /* EP0_TxSent, */
        usbd_cdc_EP0_RxReady,
        usbd_cdc_DataIn,
        usbd_cdc_DataOut,
        usbd_cdc_SOF,
        NULL,
        NULL,
        USBD_cdc_GetCfgDesc,
#ifdef USE_USB_OTG_HS
        USBD_cdc_GetOtherCfgDesc, /* use same cobfig as per FS */
#endif                            /* USE_USB_OTG_HS  */
};

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t usbd_cdc_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
    {
        /*Configuration Descriptor*/
        0x09,                              /* bLength: Configuration Descriptor size */
        USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
        USB_CDC_CONFIG_DESC_SIZ,           /* wTotalLength:no of returned bytes */
        0x00,
        0x02,                              /* bNumInterfaces: 2 interface */
        0x01,                              /* bConfigurationValue: Configuration value */
        0x00,                              /* iConfiguration: Index of string descriptor describing the configuration */
        0xC0,                              /* bmAttributes: self powered */
        0x32,                              /* MaxPower 0 mA */

        /*---------------------------------------------------------------------------*/

        /*Interface Descriptor */
        0x09,                          /* bLength: Interface Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
        /* Interface descriptor type */
        0x00, /* bInterfaceNumber: Number of Interface */
        0x00, /* bAlternateSetting: Alternate setting */
        0x01, /* bNumEndpoints: One endpoints used */
        0x02, /* bInterfaceClass: Communication Interface Class */
        0x02, /* bInterfaceSubClass: Abstract Control Model */
        0x01, /* bInterfaceProtocol: Common AT commands */
        0x00, /* iInterface: */

        /*Header Functional Descriptor*/
        0x05, /* bLength: Endpoint Descriptor size */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x00, /* bDescriptorSubtype: Header Func Desc */
        0x10, /* bcdCDC: spec release number */
        0x01,

        /*Call Management Functional Descriptor*/
        0x05, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x01, /* bDescriptorSubtype: Call Management Func Desc */
        0x00, /* bmCapabilities: D0+D1 */
        0x01, /* bDataInterface: 1 */

        /*ACM Functional Descriptor*/
        0x04, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x02, /* bDescriptorSubtype: Abstract Control Management desc */
        0x02, /* bmCapabilities */

        /*Union Functional Descriptor*/
        0x05, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x06, /* bDescriptorSubtype: Union func desc */
        0x00, /* bMasterInterface: Communication class interface */
        0x01, /* bSlaveInterface0: Data Class Interface */

        /*Endpoint 2 Descriptor*/
        0x07,                         /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
        CDC_CMD_EP,                   /* bEndpointAddress */
        0x03,                         /* bmAttributes: Interrupt */
        LOBYTE(CDC_CMD_PACKET_SZE),   /* wMaxPacketSize: */
        HIBYTE(CDC_CMD_PACKET_SZE),
#ifdef USE_USB_OTG_HS
        0x10, /* bInterval: */
#else
        0xFF, /* bInterval: */
#endif        /* USE_USB_OTG_HS */

        /*---------------------------------------------------------------------------*/

        /*Data class interface descriptor*/
        0x09,                          /* bLength: Endpoint Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
        0x01,                          /* bInterfaceNumber: Number of Interface */
        0x00,                          /* bAlternateSetting: Alternate setting */
        0x02,                          /* bNumEndpoints: Two endpoints used */
        0x0A,                          /* bInterfaceClass: CDC */
        0x00,                          /* bInterfaceSubClass: */
        0x00,                          /* bInterfaceProtocol: */
        0x00,                          /* iInterface: */

        /*Endpoint OUT Descriptor*/
        0x07,                             /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE,     /* bDescriptorType: Endpoint */
        CDC_OUT_EP,                       /* bEndpointAddress */
        0x02,                             /* bmAttributes: Bulk */
        LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */
        HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
        0x00,                             /* bInterval: ignore for Bulk transfer */

        /*Endpoint IN Descriptor*/
        0x07,                             /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE,     /* bDescriptorType: Endpoint */
        CDC_IN_EP,                        /* bEndpointAddress */
        0x02,                             /* bmAttributes: Bulk */
        LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */
        HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
        0x00                              /* bInterval: ignore for Bulk transfer */
};

#ifdef USE_USB_OTG_HS
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_OtherCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
    {
        0x09, /* bLength: Configuration Descriptor size */
        USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
        USB_CDC_CONFIG_DESC_SIZ,
        0x00,
        0x02, /* bNumInterfaces: 2 interfaces */
        0x01, /* bConfigurationValue: */
        0x04, /* iConfiguration: */
        0xC0, /* bmAttributes: */
        0x32, /* MaxPower 100 mA */

        /*Interface Descriptor */
        0x09,                          /* bLength: Interface Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
        /* Interface descriptor type */
        0x00, /* bInterfaceNumber: Number of Interface */
        0x00, /* bAlternateSetting: Alternate setting */
        0x01, /* bNumEndpoints: One endpoints used */
        0x02, /* bInterfaceClass: Communication Interface Class */
        0x02, /* bInterfaceSubClass: Abstract Control Model */
        0x01, /* bInterfaceProtocol: Common AT commands */
        0x00, /* iInterface: */

        /*Header Functional Descriptor*/
        0x05, /* bLength: Endpoint Descriptor size */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x00, /* bDescriptorSubtype: Header Func Desc */
        0x10, /* bcdCDC: spec release number */
        0x01,

        /*Call Management Functional Descriptor*/
        0x05, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x01, /* bDescriptorSubtype: Call Management Func Desc */
        0x00, /* bmCapabilities: D0+D1 */
        0x01, /* bDataInterface: 1 */

        /*ACM Functional Descriptor*/
        0x04, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x02, /* bDescriptorSubtype: Abstract Control Management desc */
        0x02, /* bmCapabilities */

        /*Union Functional Descriptor*/
        0x05, /* bFunctionLength */
        0x24, /* bDescriptorType: CS_INTERFACE */
        0x06, /* bDescriptorSubtype: Union func desc */
        0x00, /* bMasterInterface: Communication class interface */
        0x01, /* bSlaveInterface0: Data Class Interface */

        /*Endpoint 2 Descriptor*/
        0x07,                         /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
        CDC_CMD_EP,                   /* bEndpointAddress */
        0x03,                         /* bmAttributes: Interrupt */
        LOBYTE(CDC_CMD_PACKET_SZE),   /* wMaxPacketSize: */
        HIBYTE(CDC_CMD_PACKET_SZE),
        0xFF,                         /* bInterval: */

        /*---------------------------------------------------------------------------*/

        /*Data class interface descriptor*/
        0x09,                          /* bLength: Endpoint Descriptor size */
        USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
        0x01,                          /* bInterfaceNumber: Number of Interface */
        0x00,                          /* bAlternateSetting: Alternate setting */
        0x02,                          /* bNumEndpoints: Two endpoints used */
        0x0A,                          /* bInterfaceClass: CDC */
        0x00,                          /* bInterfaceSubClass: */
        0x00,                          /* bInterfaceProtocol: */
        0x00,                          /* iInterface: */

        /*Endpoint OUT Descriptor*/
        0x07,                         /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
        CDC_OUT_EP,                   /* bEndpointAddress */
        0x02,                         /* bmAttributes: Bulk */
        0x40,                         /* wMaxPacketSize: */
        0x00,
        0x00,                         /* bInterval: ignore for Bulk transfer */

        /*Endpoint IN Descriptor*/
        0x07,                         /* bLength: Endpoint Descriptor size */
        USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
        CDC_IN_EP,                    /* bEndpointAddress */
        0x02,                         /* bmAttributes: Bulk */
        0x40,                         /* wMaxPacketSize: */
        0x00,
        0x00                          /* bInterval */
};
#endif                                /* USE_USB_OTG_HS  */

/**
  * @}
  */

/** @defgroup usbd_cdc_Private_Functions
  * @{
  */

/**
  * @brief  usbd_cdc_Init
  *         Initialize the CDC interface
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
uint8_t usbd_cdc_Init(void   *pdev,
                      uint8_t cfgidx)
{
    uint8_t *pbuf;

    /* Open EP IN */
    DCD_EP_Open(pdev,
                CDC_IN_EP,
                CDC_DATA_IN_PACKET_SIZE,
                USB_OTG_EP_BULK);

    /* Open EP OUT */
    DCD_EP_Open(pdev,
                CDC_OUT_EP,
                CDC_DATA_OUT_PACKET_SIZE,
                USB_OTG_EP_BULK);

    /* Open Command IN EP */
    DCD_EP_Open(pdev,
                CDC_CMD_EP,
                CDC_CMD_PACKET_SZE,
                USB_OTG_EP_INT);

    pbuf    = (uint8_t *)USBD_DeviceDesc;
    pbuf[4] = DEVICE_CLASS_CDC;
    pbuf[5] = DEVICE_SUBCLASS_CDC;

    /* Initialize the Interface physical components */
    APP_FOPS.pIf_Init();

    /* Prepare Out endpoint to receive next packet */
    DCD_EP_PrepareRx(pdev,
                     CDC_OUT_EP,
                     (uint8_t *)(USB_Rx_Buffer),
                     CDC_DATA_OUT_PACKET_SIZE);

    return USBD_OK;
}

/**
  * @brief  usbd_cdc_Init
  *         DeInitialize the CDC layer
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
uint8_t usbd_cdc_DeInit(void   *pdev,
                        uint8_t cfgidx)
{
    /* Open EP IN */
    DCD_EP_Close(pdev,
                 CDC_IN_EP);

    /* Open EP OUT */
    DCD_EP_Close(pdev,
                 CDC_OUT_EP);

    /* Open Command IN EP */
    DCD_EP_Close(pdev,
                 CDC_CMD_EP);

    /* Restore default state of the Interface physical components */
    APP_FOPS.pIf_DeInit();

    return USBD_OK;
}

/**
  * @brief  usbd_cdc_Setup
  *         Handle the CDC specific requests
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
uint8_t usbd_cdc_Setup(void          *pdev,
                       USB_SETUP_REQ *req)
{
    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {
        /* CDC Class Requests -------------------------------*/
    case USB_REQ_TYPE_CLASS:
        /* Check if the request is a data setup packet */
        if (req->wLength)
        {
            /* Check if the request is Device-to-Host */
            if (req->bmRequest & 0x80)
            {
                /* Get the data to be sent to Host from interface layer */
                APP_FOPS.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength);

                /* Send the data to the host */
                USBD_CtlSendData(pdev,
                                 CmdBuff,
                                 req->wLength);
            }
            else /* Host-to-Device request */
            {
                /* Set the value of the current command to be processed */
                cdcCmd = req->bRequest;
                cdcLen = req->wLength;

                /* Prepare the reception of the buffer over EP0
          Next step: the received data will be managed in usbd_cdc_EP0_TxSent() 
          function. */
                USBD_CtlPrepareRx(pdev,
                                  CmdBuff,
                                  req->wLength);
            }
        }
        else /* No Data request */
        {
            /* Transfer the command to the interface layer */
            APP_FOPS.pIf_Ctrl(req->bRequest, NULL, 0);
        }

        return USBD_OK;

    default:
        USBD_CtlError(pdev, req);
        return USBD_FAIL;

        /* Standard Requests -------------------------------*/
    case USB_REQ_TYPE_STANDARD:
        switch (req->bRequest)
        {
        case USB_REQ_GET_DESCRIPTOR:
            USBD_CtlError(pdev, req);
            return USBD_FAIL;

        case USB_REQ_GET_INTERFACE:
            USBD_CtlSendData(pdev,
                             (uint8_t *)&usbd_cdc_AltSet,
                             1);
            break;

        case USB_REQ_SET_INTERFACE:
            if ((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM)
            {
                usbd_cdc_AltSet = (uint8_t)(req->wValue);
            }
            else
            {
                /* Call the error management function (command will be nacked */
                USBD_CtlError(pdev, req);
            }
            break;
        }
    }
    return USBD_OK;
}

/**
  * @brief  usbd_cdc_EP0_RxReady
  *         Data received on control endpoint
  * @param  pdev: device instance
  * @retval status
  */
uint8_t usbd_cdc_EP0_RxReady(void *pdev)
{
    if (cdcCmd != NO_CMD)
    {
        /* Process the data */
        APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen);

        /* Reset the command variable to default value */
        cdcCmd = NO_CMD;
    }

    return USBD_OK;
}


/**
  * @brief  usbd_audio_DataIn
  *         Data sent on non-control IN endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t usbd_cdc_DataIn(void *pdev, uint8_t epnum)
{
    uint16_t USB_Tx_ptr;
    uint16_t USB_Tx_length;

    if (USB_Tx_State == USB_CDC_BUSY)
    {
        if (APP_Rx_length == 0)
        {
            USB_Tx_State = USB_CDC_IDLE;
        }
        else
        {
            if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE)
            {
                USB_Tx_ptr    = APP_Rx_ptr_out;
                USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;

                APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
                APP_Rx_length  -= CDC_DATA_IN_PACKET_SIZE;
            }
            else
            {
                USB_Tx_ptr    = APP_Rx_ptr_out;
                USB_Tx_length = APP_Rx_length;

                APP_Rx_ptr_out += APP_Rx_length;
                APP_Rx_length   = 0;
                if (USB_Tx_length == CDC_DATA_IN_PACKET_SIZE)
                {
                    USB_Tx_State = USB_CDC_ZLP;
                }
            }

            /* Prepare the available data buffer to be sent on IN endpoint */
            DCD_EP_Tx(pdev,
                      CDC_IN_EP,
                      (uint8_t *)&APP_Rx_Buffer[USB_Tx_ptr],
                      USB_Tx_length);
            return USBD_OK;
        }
    }

    /* Avoid any asynchronous transfer during ZLP */
    if (USB_Tx_State == USB_CDC_ZLP)
    {
        /*Send ZLP to indicate the end of the current transfer */
        DCD_EP_Tx(pdev,
                  CDC_IN_EP,
                  NULL,
                  0);

        USB_Tx_State = USB_CDC_IDLE;
    }
    return USBD_OK;
}

/**
  * @brief  usbd_cdc_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t usbd_cdc_DataOut(void *pdev, uint8_t epnum)
{
    uint16_t USB_Rx_Cnt;

    /* Get the received data buffer and update the counter */
    USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE *)pdev)->dev.out_ep[epnum].xfer_count;

    /* USB data will be immediately processed, this allow next USB traffic being 
  NAKed till the end of the application Xfer */
    APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt);

    /* Prepare Out endpoint to receive next packet */
    DCD_EP_PrepareRx(pdev,
                     CDC_OUT_EP,
                     (uint8_t *)(USB_Rx_Buffer),
                     CDC_DATA_OUT_PACKET_SIZE);

    return USBD_OK;
}

/**
  * @brief  usbd_audio_SOF
  *         Start Of Frame event management
  * @param  pdev: instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t usbd_cdc_SOF(void *pdev)
{
    static uint32_t FrameCount = 0;

    if (FrameCount++ == CDC_IN_FRAME_INTERVAL)
    {
        /* Reset the frame counter */
        FrameCount = 0;

        /* Check the data to be sent through IN pipe */
        Handle_USBAsynchXfer(pdev);
    }

    return USBD_OK;
}

/**
  * @brief  Handle_USBAsynchXfer
  *         Send data to USB
  * @param  pdev: instance
  * @retval None
  */
static void Handle_USBAsynchXfer(void *pdev)
{
    uint16_t USB_Tx_ptr;
    uint16_t USB_Tx_length;

    if (USB_Tx_State == USB_CDC_IDLE)
    {
        if (APP_Rx_ptr_out == APP_RX_DATA_SIZE)
        {
            APP_Rx_ptr_out = 0;
        }

        if (APP_Rx_ptr_out == APP_Rx_ptr_in)
        {
            USB_Tx_State = USB_CDC_IDLE;
            return;
        }

        if (APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */
        {
            APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;
        }
        else
        {
            APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;
        }
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
        APP_Rx_length &= ~0x03;
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */

        if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE)
        {
            USB_Tx_ptr    = APP_Rx_ptr_out;
            USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;

            APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
            APP_Rx_length  -= CDC_DATA_IN_PACKET_SIZE;
            USB_Tx_State    = USB_CDC_BUSY;
        }
        else
        {
            USB_Tx_ptr    = APP_Rx_ptr_out;
            USB_Tx_length = APP_Rx_length;

            APP_Rx_ptr_out += APP_Rx_length;
            APP_Rx_length   = 0;
            if (USB_Tx_length == CDC_DATA_IN_PACKET_SIZE)
            {
                USB_Tx_State = USB_CDC_ZLP;
            }
            else
            {
                USB_Tx_State = USB_CDC_BUSY;
            }
        }

        DCD_EP_Tx(pdev,
                  CDC_IN_EP,
                  (uint8_t *)&APP_Rx_Buffer[USB_Tx_ptr],
                  USB_Tx_length);
    }
}

/**
  * @brief  USBD_cdc_GetCfgDesc 
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t *USBD_cdc_GetCfgDesc(uint8_t speed, uint16_t *length)
{
    *length = sizeof(usbd_cdc_CfgDesc);
    return usbd_cdc_CfgDesc;
}

/**
  * @brief  USBD_cdc_GetCfgDesc 
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
#ifdef USE_USB_OTG_HS
static uint8_t *USBD_cdc_GetOtherCfgDesc(uint8_t speed, uint16_t *length)
{
    *length = sizeof(usbd_cdc_OtherCfgDesc);
    return usbd_cdc_OtherCfgDesc;
}
#endif
/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
